// Module R6
// Authors: James McKowen

#include "R6.H"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <dos.h>
#include "mpx_supt.h"

pq ready, suspended_ready, blocked, suspended_blocked;
ppcb cop;
unsigned short ss_save, sp_save, new_ss, new_sp;
char sys_stack[PCB_STACK_SIZE];
iocb com,terminal;
params *param_ptr;
ppqn Find_PCB(char *name);
date_rec *date_p;
iod *temp;
// prototypes

void Free_PCB(ppcb ptr);
void Remove_PCB(ppcb p);
void interrupt dispatcher();
void getDate(int argc, char **argv);
void getDateWord(int argc, char **argv);
void version(int argc, char **argv);
void setDate(int argc, char **argv);
void quit(int argc, char **argv);
void help (int argc, char **argv);
void directory(int argc, char **argv);
void load_program(char*, int);
void load (int argc, char **argv);

void thread_print(const char *message) {

	int length;
	
	length = strlen(message);
	sys_req(WRITE,TERMINAL, message, &length);
	
}

void r2_init () {

	ready.head=NULL;
	ready.tail=NULL;
	blocked.head=NULL;
	blocked.tail=NULL;
	suspended_blocked.head=NULL;
	suspended_blocked.tail=NULL;
	suspended_ready.head=NULL;
	suspended_ready.tail=NULL;
	
	ready.length=0;
	blocked.length=0;
	suspended_blocked.length=0;
	suspended_ready.length=0;
	
}

int is_string_alpha(char *string) {
	
	int i;

	for (i=0; i < strlen(string); i++)
		if (!isalpha(string[i]) && !isdigit(string[i])) return 0;
		
	return 1;
	
}

int is_string_numbers_only(char *string) {

	int i;
	
	for (i=0; i < strlen(string); i++)
		if (!isdigit(string[i])) return 0;
	
	return 1;
}

ppcb Allocate_PCB()
{
	ppcb pcb_ptr;
	
	pcb_ptr = sys_alloc_mem(sizeof(pcb));
	pcb_ptr->stack_base = sys_alloc_mem(PCB_STACK_SIZE);
	pcb_ptr->stack_top = pcb_ptr->stack_base + PCB_STACK_SIZE - sizeof(struct context);
	memset(pcb_ptr->stack_base, '\0', PCB_STACK_SIZE);
	return(pcb_ptr);
}

void Free_PCB(ppcb ptr)
{
	if (ptr->class == APPLICATION) {
	sys_free_mem(ptr->stack_base);
	sys_free_mem(ptr->load);
	sys_free_mem(ptr);
	}
}

// Find needs to work before testing

ppcb Setup_PCB(char *name, int priority, int class) {

	ppcb new;

	if (priority < -128 || priority > 128) {
		thread_print("Priority must be between -127 and 128.\n");
		return NULL;
	}

	if (class != APPLICATION && class != SYSTEM) {
		thread_print("Class value must either be 0 for application, or 1 for system\n");
		return NULL;
	}

	if (Find_PCB(name) != NULL || strlen(name) > PCB_NAME_SIZE || strlen(name) == 0) {
		printf("\nProcess with name \"%s\" already exists or name length invalid.\n", name);
		return NULL;
	}

	new = Allocate_PCB();

	strcpy(new->name, name);
	new->priority = priority;
	new->class = class;

	return new;

}

// Note, return value of null could mean name invalid or not found

ppqn Find_PCB(char* name) {

	ppqn temp;
	int i;

	if (strlen(name) == 0 || strlen(name) > PCB_NAME_SIZE) {
		thread_print("\nProcess names are between 1 and 32 characters in length.\n");
		return NULL;
	}
	
	if (!is_string_alpha(name)) {
		thread_print("\nProcess name must not contain symbols.\n");
		return NULL;
	}

	// check ready
	temp = ready.head;
	for (i=0; i < ready.length; i++) {
		if (strcmp(temp->data->name, name)==0) return temp;
		temp = temp->right;
	}

	// check blocked
	temp = blocked.head;
	for (i=0; i < blocked.length; i++) {
		if (strcmp(temp->data->name, name)==0) return temp;
		temp = temp->right;
	}

	// check suspended ready
	temp = suspended_ready.head;
	for (i=0; i < suspended_ready.length; i++) {
		if (strcmp(temp->data->name, name)==0) return temp;
		temp = temp->right;
	}
	// check suspended blocked
	temp = suspended_blocked.head;
	for (i=0; i < suspended_blocked.length; i++) {
		if (strcmp(temp->data->name, name)==0) return temp;
		temp = temp->right;
	}
	return NULL;

}

void Insert_PCB(ppcb p) {

	ppqn node,new_node;
	new_node = sys_alloc_mem(sizeof(pqn));
	if (new_node == NULL) thread_print("Out of memory.\n");
	new_node->data = p;
	
	switch (p->state) {
	
		case READY:
		
			if (ready.head == NULL) {
				
				ready.head = new_node;
				ready.tail = new_node;
				ready.length=1;
				
			} else {
				
				node = ready.head;
				
				while (node != NULL) {
					
					if (node->data->priority > p->priority) {
					
						if (node->left == NULL) {
							
							ready.head=new_node;
							new_node->right=node;
							node->left=new_node;
							new_node->left=NULL;
							ready.length++;							
							return;
							
						} else {
						
							node->left->right = new_node;
							new_node->left = node->left;
							new_node->right = node;
							node->left = new_node;
							ready.length++;
							return;
							
						}
					}
					node = node->right;
				}
				
				ready.tail->right = new_node;
				new_node->left=ready.tail;
				ready.tail=new_node;
				ready.length++;
			}
			
			break;
			
		case BLOCKED:
		
			if (blocked.head == NULL) {
			
				blocked.head = new_node;
				blocked.tail = new_node;
				blocked.length=1;
				
			} else {
			
				blocked.tail->right=new_node;
				new_node->left = blocked.tail;
				blocked.tail = new_node;
				new_node->right=NULL;
				blocked.length++;
			
			}
			break;
			
		case SUSPENDED_BLOCKED:
		
			if (suspended_blocked.head == NULL) {
				suspended_blocked.head = new_node;
				suspended_blocked.tail = new_node;
			} else {
				suspended_blocked.tail->right=new_node;
				new_node->left = suspended_blocked.tail;
				suspended_blocked.tail = new_node;
			}
			suspended_blocked.length++;
			break;
			
		case SUSPENDED_READY:
			// queue is empty, just change the head
			if (suspended_ready.head == NULL) {
				suspended_ready.head = new_node;
				suspended_ready.tail = new_node;
				suspended_ready.length=1;
				}
			else {
				node = suspended_ready.head;
				while (node != NULL) {
					//thread_print("Node value: %d, P value: %d\n", node->data->priority, p->priority);
					if (node->data->priority > p->priority) {
						if (node->left == NULL) {
							suspended_ready.head=new_node;
							new_node->right=node;
							node->left=new_node;
							new_node->left=NULL;
							suspended_ready.length++;
							return;
						} else {
							node->left->right = new_node;
							new_node->left = node->left;
							new_node->right = node;
							node->left = new_node;
							suspended_ready.length++;
							return;
						}
					}
					node = node->right;
				}
				suspended_ready.tail->right = new_node;
				new_node->left=suspended_ready.tail;
				suspended_ready.tail=new_node;
				suspended_ready.length++;
			}
			
			break;
	
	}
}

void Remove_PCB (ppcb p) {

	ppqn node = Find_PCB(p->name);
	
	if (node == NULL) {
		thread_print("Couldn't find a pcb with that name.\n");
		return;
	}

	if (node->left == NULL) {
	
		if (node->data->state == READY)
			ready.head=node->right;
		else if (node->data->state == BLOCKED)
			blocked.head=node->right;
		else if (node->data->state == SUSPENDED_READY)
			suspended_ready.head=node->right;
		else if (node->data->state == SUSPENDED_BLOCKED)
			suspended_blocked.head=node->right;
		
	}
	
	if (node->data->state == READY) ready.length--;
	else if (node->data->state == BLOCKED) blocked.length--;
	else if (node->data->state == SUSPENDED_READY) suspended_ready.length--;
	else if (node->data->state == SUSPENDED_BLOCKED) suspended_blocked.length--;
	
	node->left->right=node->right;
	node->right->left = node->left;
	
	if (ready.length==0) ready.head=NULL;
	if (blocked.length==0) blocked.head=NULL;
	if (suspended_ready.length==0) suspended_ready.head=NULL;
	if (suspended_blocked.length==0) suspended_blocked.head=NULL;
	
	sys_free_mem(node);
	
}

void suspend (int argc, char **argv) {

	ppqn node;
	ppcb data;
	
	if (argc != 1) {

			thread_print("Usage: suspend process_name\n");
			return;
			
	}
	
	if (strlen(argv[0]) > PCB_NAME_SIZE) {
		thread_print("\nProcess names are between 1 and 32 characters in length.\n");
		return;
	}
	
	if (!is_string_alpha(argv[0])) {
		thread_print("\nProcess name must not contain symbols.\n");
		return;
	}
	
	node = Find_PCB(argv[0]);
	
	if (node == NULL) {
			thread_print("\nPCB can't be located.\n");
			return;
	}	
	
	if (node->data->class == SYSTEM) {
		thread_print("Unable to suspend system processes.\n");
		return;
	}
	
	if (node->data->state == READY) {
	
		data=node->data;
		Remove_PCB(node->data);
		data->state = SUSPENDED_READY;
		Insert_PCB(data);
		thread_print("\nProcess successfully suspended.\n");
		
	} else
	if (node->data->state == BLOCKED) {
	
		data=node->data;
		Remove_PCB(node->data);
		data->state = SUSPENDED_BLOCKED;
		Insert_PCB(data);
		thread_print("\nProcess successfully suspended.\n");
		
	}

}

void resume (int argc, char **argv) {

	ppqn node;
	ppcb data;

	if (argc != 1) {
	
		thread_print("Usage: resume process_name\n");
		return;
		
	}

	if (strlen(argv[0]) > PCB_NAME_SIZE) {
		thread_print("\nProcess names are between 1 and 32 characters in length.\n");
		return;
	}
	
	if (!is_string_alpha(argv[0])) {
		thread_print("\nProcess name must not contain symbols.\n");
		return;
	}
	
	node = Find_PCB(argv[0]);
	
	if (node == NULL) {
			thread_print("\nPCB can't be located.\n");
			return;
	}
	
	
	if (node->data->class == SYSTEM) {
		thread_print("Unable to resume system processes.\n");
		return;
	}
	
	data=node->data;
	
	if (data->state == SUSPENDED_READY) {
		Remove_PCB(node->data);
		data->state = READY;		
		Insert_PCB(data);
		thread_print("\nProcess successfully resumed.\n");
	} else
	if (data->state == SUSPENDED_BLOCKED) {
		Remove_PCB(node->data);
		data->state = BLOCKED;
		Insert_PCB(data);
		thread_print("\nProcess successfully resumed.\n");
	}
		
}

void Set_Priority (int argc, char **argv) {

	ppqn node;
	ppcb data;
	int i;
	
	if (argc != 2) {
	
		thread_print("Usage: Set_Priority process_name priority\n");
		return;
	
	}
	
	if (strlen(argv[0]) == 0 || strlen(argv[0]) > PCB_NAME_SIZE) {
		thread_print("\nProcess names are between 1 and 32 characters in length.\n");
		return;
	}
	
	if (!is_string_alpha(argv[0])) {
		thread_print("\nProcess name must not contain symbols.\n");
		return;
	}
	
	node = Find_PCB(argv[0]);
	
	if (node == NULL) {
		thread_print("\nPCB can't be located.\n");
		return;
	}
	
	if (atoi(argv[1]) < -127 || atoi(argv[1]) > 128) {
	
		thread_print("Priority value must be between -127 and 128.\n");
		return;
	
	}
	
	if (node->data->class == SYSTEM) {
	
		thread_print("Cannot change priority of system classes.\n");
		return;
	}
	
	
	for (i=0; i < strlen(argv[1]); i++)
		if (!isdigit(argv[1][i]) && argv[1][i] != '-') {
			thread_print("Priority may only contain numbers and the - symbol.\n");
			return;
		}
	
	data = node->data;
	Remove_PCB(node->data);
	data->priority = atoi(argv[1]);
	Insert_PCB(data);
	thread_print("Priority changed successfully.\n");
		
}

void Show_Ready(int argc, char **argv) {

	ppqn temp;
	int i;
	
	if (ready.head != NULL) {
		temp=ready.head;
		while (temp != NULL) {
				printf("Name: %10s, Class: %d, Size: %d, Priority: %4d, State: %d\n", temp->data->name, temp->data->class, temp->data->memory_size, temp->data->priority, temp->data->state);
				temp=temp->right;
		}
				
	} else thread_print("No processes in ready queue.\n");

}

void Show_Suspended_Ready(int argc, char **argv) {

	ppqn temp;
	int i;
	
	if (suspended_ready.head != NULL) {
		temp=suspended_ready.head;
		for (i=0; i < suspended_ready.length; i++) {
				printf("Name: %s, Class: %d, Size: %d, Priority: %d, State: %d\n", temp->data->name, temp->data->class, temp->data->memory_size, temp->data->priority, temp->data->state);
				temp=temp->right;
		}
				
	} else thread_print("No processes in suspended ready queue.\n");

}

void Show_Blocked(int argc, char **argv) {

	ppqn temp;
	int i;
	
	if (blocked.head != NULL) {
		temp=blocked.head;
		for (i=0; i < blocked.length; i++) {
				printf("Name: %s, Class: %d, Size: %d, Priority: %d, State: %d\n", temp->data->name, temp->data->class, temp->data->memory_size, temp->data->priority, temp->data->state);
				temp=temp->right;
		}
	
	} else thread_print("No processes in blocked queue.\n");

}

void Show_Suspended_Blocked(int argc, char **argv) {

	ppqn temp;
	int i;
	
	if (suspended_blocked.head != NULL) {
		temp=suspended_blocked.head;
		for (i=0; i < suspended_blocked.length; i++) {
				printf("Name: %s, Class: %d, Size: %d, Priority: %d, State: %d\n", temp->data->name, temp->data->class, temp->data->memory_size, temp->data->priority, temp->data->state);
				temp=temp->right;
		}
				
	} else thread_print("No processes in suspended blocked queue.\n");


}

void Show_PCB(int argc, char **argv) {

	ppqn temp;
	char name[256];
	int name_length;
	name_length = 256;
	
	thread_print("Please enter the process name: ");	
	sys_req(READ, TERMINAL, name, &name_length);

	name[strlen(name)-1]='\0';

	if (strlen(name) == 0 || strlen(name) > PCB_NAME_SIZE) {
		thread_print("\nProcess names are between 1 and 32 characters in length.\n");
		return;
	}
	
	if (!is_string_alpha(name)) {
		thread_print("\nProcess name must not contain symbols.\n");
		return;
	}
	
	if (strlen(name) == 0) {
		thread_print("You must supply a process name.\n");
		return;
	}
	
	temp = Find_PCB(name);

	if (temp == NULL) {
		thread_print("\nProcess not found.\n");
		return;
	}
	
	printf("\nName: %s, Class: %d, Size: %d, Priority: %d, State: %d\n", temp->data->name, temp->data->class, temp->data->memory_size, temp->data->priority, temp->data->state);
	
}

void Show_All (int argc, char **argv) {

	thread_print("Ready queue:\n");
	Show_Ready(argc,argv);
	thread_print("\nBlocked queue:\n");
	Show_Blocked(argc,argv);
	thread_print("\nSuspended-ready queue:\n");
	Show_Suspended_Ready(argc,argv);
	thread_print("\nSuspended-blocked queue:\n");
	Show_Suspended_Blocked(argc,argv);

}

//*********************************************************************************************
// R1 MATERIAL BELOW THIS LINE
//*********************************************************************************************

// Help
// Lists all functions available in the system
// or displays information on a specifc function.
// Paramaters: argc, argv
// Returns: void
// This aglorithm uses selection statements to determine
// if they are trying to access the main help function or
// the help function for individual functions.

void help (int argc, char **argv) {

  FILE *inFilePtr;
  char ending [] = ".txt";
  int sizeFile;
  char fileLine[128];

  //Accessing broad help function

  if (argc > 1) {

	thread_print("This function takes zero or one argument. Type \"help\" for more information.\n");
	return;
	
  }

  if(argc == 0)
    {
      char helpFilename[] = "help.txt";

      if((inFilePtr = fopen(helpFilename, "r")) != NULL)
	{
	  while(fgets (fileLine, sizeof( fileLine), inFilePtr) != NULL)
	    {
	      printf("%s", fileLine);
	    }
	  }
      else{
	thread_print("Help file for command list not found.");
      }

      thread_print("\n");

    }
  //Accessing specialized help functions
  else if(argc == 1)
    {
      char *name = argv[0];
      char *filename = malloc(strlen(name) + strlen(ending) + 2);
      strcpy(filename, name);
      strcat(filename, ending);

	  if (strlen(filename) >= 13)
		strcpy(filename+6, "~1.txt\0");

      if((inFilePtr = fopen(filename, "r")) != NULL)
	{

	  while(fgets (fileLine, sizeof fileLine, inFilePtr) != NULL)
	    {
	      printf("%s", fileLine);
	    }
	}

	else printf("Help file not found for command \"%s\".\n", argv[0]);

    } else
	thread_print("Usage: help -or- help command\n");
}

// displayDate
// Displays the current date in numerical format.
// Parameters: argc, argv - neither are used to make this function work.
// Returns: void
// Uses the system function for get date.

void getDate (int argc, char **argv) {

     argc = argc;
     argv = argv;

     sys_get_date(date_p); //This is the MPX support routine needed to get the
			   //date
     printf("%d/%d/%d\n", date_p->month, date_p->day, date_p->year);

}

// displayDateWord
// Is the same as displayDate(), it just converts the month
// into the word for that month
// Parameters: argc, argv - neither are used to make this function work.
// Uses the system function for get date.

void getDateWord (int argc, char **argv) {

     argc = argc;
     argv = argv;

     sys_get_date(date_p);

     thread_print("The Current Date Set:\n");
     thread_print("MONTH:  ");
     if(date_p->month == 1)
	thread_print("January\n");
     else if(date_p->month == 2)
	thread_print("February\n");
     else if(date_p->month == 3)
	thread_print("March\n");
     else if(date_p->month == 4)
	thread_print("April\n");
     else if(date_p->month == 5)
	thread_print("May\n");
     else if(date_p->month == 6)
	thread_print("June\n");
     else if(date_p->month == 7)
	thread_print("July\n");
     else if(date_p->month == 8)
	thread_print("August\n");
     else if(date_p->month == 9)
	thread_print("September\n");
     else if(date_p->month == 10)
	thread_print("October\n");
     else if(date_p->month == 11)
	thread_print("November\n");
     else if(date_p->month == 12)
       thread_print("December\n");

     printf("DAY:    %d\n", date_p->day);
     printf("YEAR:   %d\n", date_p->year);

}

// chageDate
// Sets the date to the variables that are passed to it.
// Parameters: month, day, year
// Returns: void
// Uses the system function save_date.

void setDate(int argc, char **argv)
{
  int month = atoi(argv[0]);
  int day =  atoi(argv[1]);

  int year = 1;

  int temp = 1;

  date_rec reset;
  date_rec *save_date;

  while( argv[2] == '0')
  {
     argv[2]++;
  }
  if(strlen == 0)
  {
     thread_print("The year can not be zero\n");
     return;
  }
  if(strlen(argv[2]) > 4)
  {
     thread_print("The year is too long to be set.\n");
     return;
  }

  year = atol(argv[2]);

  thread_print("Date to be changed: ");
  getDate(0, NULL);
  thread_print("\n");


  save_date->month = month;
  save_date->day = day;
  save_date->year = year;

     //checking to make sure the variables passed to the function are within
     //the correct constraints

     if (argc > 3)
     {
     thread_print("Note: function only requires 3 arguments. Type \"help date\" for more info.\n\n");

     }

     if(month < 1 || month > 12) //checking the month
     {
	 thread_print("Values for 'month' must be between 1 and 12\n");
	 return;
     }

     if(day < 1 || day > 31) //checking the day
     {
	 thread_print("Acceptable values for 'days' in this month are between 1 and 31\n");
	 return;
     }

     if (year < 0)
     {
	 thread_print("Error: Year must be > 0.\n");
	 return;
	 }

     //checking the 30 day max for certain months

     if((day > 30) && (month == 4 || month == 6 || month == 9 || month == 11))
     {
	 thread_print("Acceptable values for 'days' in this month are between 1 and 30\n");
	 return;
     }

     if(month == 2) //checking for february problems and leap year
     {
	 if((year%4 != 0) && day > 28)
	 {
	     thread_print("It is not a leap year.\n");
	     return;
	 }
	 else if((year%4 == 0) && day > 29)
	 {
	     thread_print("There are only 29 days in February on leap year.\n");
	     return;
	 }
     }

     temp = sys_set_date(save_date);

     //if(save_date->month == date_p->month && save_date->day == date_p->day && save_date->year == date_p->year)
     //{
     //	 temp = 0;
     //}

     if(temp != 0)
     {
	 thread_print("You have entered an invalid Date.\n");

     } else {

	 thread_print("Date Set Successfully!\n");
	 thread_print("\n");
	 thread_print("The new current date is:  ");
	 getDate(0, NULL);
	 thread_print("\n");

     }

     argc = argc;
}

// Version
// Displays the current version, along with the system authors.
// Parameters: argc, argv - neither are used to make this function run.
// Returns: void
// Uses the thread_print function.
void version (int argc, char **argv) {

     if (argc > 0)
		thread_print("Note: this function requires no arguments.\n\n");

     thread_print("VERSION INFORMATION\n");
     thread_print("\n");
     thread_print("Version: 6\n");
     thread_print("Deliverable: R6\n");
     thread_print("Author: James McKowen\n");
     thread_print("Last Modified: 12-9-2011\n");

}

// directory
// Lists all files with mpx extensions.
// Parameters: argc, argv - not needed for this function.
// Returns: void
// First uses the system function sys_open_dir to open the directory
// then uses the system function sys_get_entry to pull the different
// files and check to see if they have the mpx extension

void directory (int argc, char **argv) {

	char *directory;
	int  result;

	if (argc > 1) {
		thread_print("\n\tUsage: directory {path}\n\n");
		return;
	}

	// Attempt to open the user's requested directory.
	directory = (argc == 0 ? DEFAULT_DIRECTORY : argv[0]);
	result = sys_open_dir(directory);

	// If the attempt succeeded, print the files in the directory and their
	// sizes to the standard output.
	if (result == OK) {
		char  filename[BUFFER_SIZE];
		int   written = 0;
		short pause = 0;
		long  file_size;

		while (
			sys_get_entry(
				filename,
				BUFFER_SIZE,
				&file_size
			) != ERR_SUP_NOENTR
		) {
			// If a specific number of lines have been printed, pause the
			// printing so the user can catch up.
			if (pause) {
				system("pause");
				gotoxy(1, wherey() - 2);
				thread_print("                               ");
				gotoxy(1, wherey());
				pause = 0;
			}

			// Write the file and its size to the terminal.
			printf("%-20s%d bytes\n", filename, file_size);

			// If a specific number of lines have been printed, set the pause
			// flag.
			if ((++written % DISPLAY_AT_A_TIME) == 0) {
				pause = 1;
			}
		}

		// If no files were printed, write a message to the screen.
		if (written == 0) {
			thread_print("No .MPX files were found in the directory.\n");
		}
	}

	// Otherwise, print error messages explaining the failure.
	else {
		switch (result) {

			case ERR_SUP_DIROPN:
				printf(
					"\n\tError: could not open the directory \"%s\".\n\n",
					directory
				);
				break;

			case ERR_SUP_INVDIR:
				printf(
					"\n\tError: \"%s\" is an invalid directory.\n\n",
					directory
				);
				break;
		}
	}

	sys_close_dir();

}

// quit
// Exits the system.
// Parameters: argc, argv - not needed to use this command.
// Returns: void.
// Uses the system function sys_req to make sure that the user
// wants to exit the system.

void quit (int argc, char **argv) {

  char buf[4];
  iod *temp;
  int buf_size = 4;
  char answer = 'y';

  thread_print("Are you sure you want to exit? (y/*)\n\n");
  thread_print("> ");

  sys_req(READ, TERMINAL, buf, &buf_size);

  if(answer==buf[0])
    {
	thread_print("\nThanks for using MPX.\n\n");
	system("pause");
	
	//free iods
	temp = com.head;
	while (temp != NULL) {
		sys_free_mem(temp);
		temp=temp->next;
	}
	temp = terminal.head;
	while (temp != NULL) {
		sys_free_mem(temp);
		temp=temp->next;
	}
	//free iocbs
	sys_free_mem(&com);
	sys_free_mem(&terminal);
	//close devices
	trm_close();
	com_close();
	//exit
	sys_exit();
    }

    argc = argc;
    argv = argv;
}

// run
// The command line interface that allows the user to input a function
// and then calls that particular function.
// Parameters: none
// Return: void
// The function reads the command in through the command line and
// sends its parameters to the correct function to be processed

void terminate (int argc, char **argv) {

	ppqn node;
	
	node=NULL;
	
	if (argc != 1) {
		thread_print("Usage: terminate pcb_name\n");
		return;
	}
	
	if (!is_string_alpha(argv[0])) {
	
		thread_print("Process names cannot contain symbols.\n");
		return;
	}
	
	node = Find_PCB(argv[0]);
	
	if (node == NULL) {
		thread_print("Process not found.\n");
		return;
	}
	
	if (node->data->class == SYSTEM) {
		thread_print("Unable to terminate system processes.\n");
		return;
	}
	
	Remove_PCB(node->data);
	Free_PCB(node->data);

}

void go (int argc, char **argv) {

	sys_req(IDLE, 0, NULL, NULL);

}

void run () {

	int found;
	char buffer[BUFFER_SIZE];
	char separator[]=" ";
	char *command, *token;
	int size=BUFFER_SIZE;
	int numargs=0;
	int i;
	char *args[MAX_ARGS];

	char function_names[NUM_FUNCTIONS][COMMAND_LENGTH] = {"terminate", "help", "version", "getDate", "getDateWord", "setDate", "directory", "quit", "suspend", "resume", "Set_Priority", "Show_Ready", "Show_Blocked", "Show_Suspended_Blocked", "Show_Suspended_Ready", "Show_PCB", "Show_All", "load", "go"};
	int (*functions[NUM_FUNCTIONS])(int, char**) = {&terminate, &help, &version, &getDate, &getDateWord, &setDate, &directory, &quit, &suspend, &resume, &Set_Priority, &Show_Ready, &Show_Blocked, &Show_Suspended_Blocked, &Show_Suspended_Ready, &Show_PCB, &Show_All, &load, &go};

	system("cls");
	
	thread_print("\n");                               
	thread_print("    _/      _/  _/_/_/    _/      _/   \n");
	thread_print("   _/_/  _/_/  _/    _/    _/  _/      \n");
	thread_print("  _/  _/  _/  _/_/_/        _/         \n");
	thread_print(" _/      _/  _/          _/  _/        \n");
	thread_print("_/      _/  _/        _/      _/ \tby Vita Nova\n\n\n");
	
	thread_print("An MPX can have 50 arguments and be as long as 200 characters.\n\nType \"help\" for more information.\n\n");

	while (1) {

		numargs=0;

		thread_print("> ");
		
		size=BUFFER_SIZE;
		sys_req(READ, TERMINAL, buffer, &size);	
		
		
		if (strcmp(buffer, "\n") != 0) {
		
		buffer[strlen(buffer)-1]='\0';
		
		command = strtok(buffer, separator);
		token = strtok (NULL, separator);

		while (token != NULL) {
			numargs++;
			args[numargs-1]=token;
			token = strtok(NULL,separator);
		}

		found=0;

		for (i = 0; i < NUM_FUNCTIONS; i++) {

		       if (strcmp(function_names[i], command) == 0) {
			       thread_print("\n");
			       functions[i](numargs, args);
			       thread_print("\n");
			       found=1;
		       }
		}

		if (found == 0 && strcmp(command, NULL) != 0)
			thread_print("\nCommand not found.\n\n");

	}
	}
}

void load (int argc, char **argv) {

	ppqn node;
	int i;
	
	node=NULL;

	if (argc == 0) {
	
		thread_print("You must at least enter the filename.\n");
		return;
	}
	
	node = Find_PCB(argv[0]);
	
	if (node != NULL) {
		thread_print("Process already loaded.\n");
		return;
	}
	
	if (argc == 1) {
	
		load_program(argv[0], 0);
	
	}
	
	if (argc == 2) {
	
		for (i=0; i < strlen(argv[1]); i++)
		if (!isdigit(argv[1][i]) && argv[1][i] != '-') {
			thread_print("Priority may only contain numbers and the - symbol.\n");
			return;
		}
	
	
		load_program(argv[0], atoi(argv[1]));
	
	}

}

void load_program(char *filename, int priority) {

	ppcb new;
	int error, program_length, start_offset_p;
	context *c;
	
	if (!is_string_alpha(filename))
		return;
	
	if (priority < -127 || priority > 128) {
		thread_print("Priorities must be between -127 and 128.\n");
		return;
	}
	
	error = sys_check_program("\0", filename, &program_length, &start_offset_p);

	switch (error) {
	
		case ERR_SUP_NAMLNG:
			thread_print("Path too long.\n");
			return;
		case ERR_SUP_FILNFD:
			thread_print("File not found.\n");
			return;
		case ERR_SUP_FILINV:
			thread_print("File invalid.");
			return;
	}
	
	new = Setup_PCB(filename, priority, APPLICATION);
	new->state=SUSPENDED_READY;
	new->memory_size=program_length;
	new->load = (unsigned char*)sys_alloc_mem(program_length);
	new->execute = new->load + start_offset_p;
	c = (context *)new->stack_top;
	c->IP=FP_OFF(new->execute);
	c->CS=FP_SEG(new->execute);
	c->DS=_DS;
	c->ES=_ES;
	c->FLAGS=0x200;

	error = sys_load_program(new->load, new->memory_size, "\0", filename);
	
	if (error != 0) {
		thread_print("Error loading program");
		return;
	}
	
	Insert_PCB(new);
	
}

void interrupt dispatcher () {

	if (sp_save == NULL) {

		ss_save = _SS;
		sp_save = _SP;

	}

	if (ready.head!=NULL) {

		disable();
		cop = ready.head->data;
		Remove_PCB(cop);
		cop->state=RUNNING;
		new_ss=FP_SEG(cop->stack_top);
		new_sp=FP_OFF(cop->stack_top);
		_SS = new_ss;
		_SP = new_sp;
		enable();

	} else {

		disable();
		cop = NULL;
		_SS = ss_save;
		_SP = sp_save;
		sp_save = ss_save = NULL;
		enable();
	}

}

void ioscheduler() {

	iod *new = sys_alloc_mem(sizeof(iod));
	new->process = cop;
	new->name = cop->name;
	new->buffer=param_ptr->buf_addr;
	new->count=param_ptr->count_addr;
	new->request_type=param_ptr->op_code;
	new->next=NULL;

	if (param_ptr->device_id == TERMINAL) {

		if (terminal.count == 0) {

			terminal.head=new;
			terminal.tail=new;
			terminal.count=1;

			switch (new->request_type) {

				case READ:
					trm_read(terminal.head->buffer, terminal.head->count);
					break;
				case WRITE:
					trm_write(terminal.head->buffer, terminal.head->count);
					break;
				case CLEAR:
					trm_clear();
					break;
				case GOTOXY:
					trm_gotoxy(0,0);
					break;
			}

		} else {

			disable();
			terminal.tail->next = new;
			terminal.tail=new;
			terminal.count++;
			enable();
		}
	}

	if (param_ptr->device_id == COM_PORT) {

		if (com.count == 0) {

			disable();
			com.head=new;
			com.tail=new;
			com.count=1;
			enable();

			switch (new->request_type) {

				case READ:
					com_read(com.head->buffer, com.head->count);
					break;

				case WRITE:
					com_write(com.head->buffer, com.head->count);
					break;

			}

		} else {

			disable();
			com.tail->next = new;
			com.tail=new;
			com.count++;
			enable();

		}

	}

	disable();
	cop->state = BLOCKED;
	Insert_PCB(cop);
	enable();
	return;

}

void interrupt sys_call (){

	disable();
	cop->stack_top=MK_FP(_SS,_SP);
	param_ptr = (params*)((unsigned char *)MK_FP(_SS,_SP)+sizeof(context));
	new_ss = FP_SEG(&sys_stack);
	new_sp = FP_OFF(&sys_stack) + PCB_STACK_SIZE;	

	_SS = new_ss;
	_SP = new_sp;
	enable();
	
	trm_getc();
	
	// R6 stuff here
	
	if (com.event_flag == 1) {

		com.event_flag=0;
		
		Remove_PCB(com.head->process);
		com.head->process->state=READY;
		Insert_PCB(com.head->process);
		
		sys_free_mem(com.head);
		
		if (com.count == 1) {
			com.tail=NULL;
			com.head=NULL;
			com.count=0;
		}
		else {
			com.head=com.head->next;
			com.count--;
		}		
		
		if (com.head != NULL) {
		
			switch (com.head->request_type) {
			
				case READ:				
					com_read(com.head->buffer, com.head->count);				
					break;
				
				case WRITE:				
					com_write(com.head->buffer, com.head->count);				
					break;
			
			}		
		}	
	
		
	
	}
	
	if (terminal.event_flag == 1) {
	
		terminal.event_flag=0;
		
		Remove_PCB(terminal.head->process);
		terminal.head->process->state=READY;
		Insert_PCB(terminal.head->process);
		
		sys_free_mem(terminal.head);
		
		if (terminal.count == 1) {
			terminal.tail=NULL;
			terminal.head=NULL;
			terminal.count=0;
		}
		else {
			terminal.head=terminal.head->next;
			terminal.count--;
		}
			
		if (terminal.head != NULL) {
		
			switch (terminal.head->request_type) {
			
				case READ:
				
					trm_read(terminal.head->buffer, terminal.head->count);
				
				break;
				
				case WRITE:
				
					trm_write(terminal.head->buffer, terminal.head->count);
				
				break;
				
				case CLEAR:
				
					trm_clear();
				
				break;
				
				case GOTOXY:
			
					trm_gotoxy(0,0);
			
				break;
			
			}
		
		}
	
	}

	if (param_ptr->op_code == IDLE) {
		disable();
		cop->state=READY;
		Insert_PCB(cop);
		enable();
	} else if (param_ptr->op_code == EXIT) {
		Free_PCB(cop);
		cop=NULL;
	} else if (param_ptr->op_code == READ) {
		ioscheduler();
	} else if (param_ptr->op_code == CLEAR) {
		ioscheduler();
	} else if (param_ptr->op_code == GOTOXY) {
		ioscheduler();
	} else if (param_ptr->op_code == WRITE) {
		ioscheduler();
	}

	dispatcher();
}

// Main
// Initialization method
// Parameters: none
// Returns: int

int main () {

	context *npc;
	ppcb np;
	ppqn a;
	int i;
	char idle[] = "idle";
	int error;
	
	sys_init(MODULE_F);		
	
	com.head=NULL;
	com.tail=NULL;
	com.count=0;
	terminal.head=NULL;
	terminal.tail=NULL;
	terminal.count=0;
	com.event_flag=0;
	terminal.event_flag=0;
	
	ready.head=NULL;
	ready.tail=NULL;
	blocked.head=NULL;
	blocked.tail=NULL;
	suspended_ready.head=NULL;
	suspended_ready.tail=NULL;
	suspended_blocked.head=NULL;
	suspended_blocked.tail=NULL;
	ss_save = NULL;
	sp_save = NULL;
	new_ss = NULL;
	new_sp = NULL;	
	
	sys_set_vec(sys_call);
	error = com_open(&com.event_flag, 1200);
	error = trm_open(&terminal.event_flag);	
	
	np = Setup_PCB("comhan", 0, SYSTEM);
	npc = (context*)(np->stack_top);
	npc->IP = FP_OFF(run);
	npc->CS = FP_SEG(run);
	npc->FLAGS = 0x200;
	npc->DS = _DS;
	npc->ES = _ES;
	np->state=READY;
	Insert_PCB(np);

	// load and resume idle
	load_program(idle, 127);
	a = Find_PCB(idle);
	Remove_PCB(a->data);
	a->data->state=READY;
	a->data->class=SYSTEM;
	Insert_PCB(a->data);
	dispatcher();
	
	return 0;

}